﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Threading;
using BattleShips.Common;
using System.Net;
using System.IO;

namespace BattleShips.GameClients {
    public partial class BattleShips : Form, IUserCommands {
        public BattleShips() {
            InitializeComponent();
        }

        TcpClient client;

        string Username, RoomName;
        int nrOfSelectedFields = 0;
        List<string> SelectedFields = new List<string>();

        private void BattleShips_Load(object sender, EventArgs e) {

            client = new TcpClient();

            WelcomeScreen userWelcomeScreen = new WelcomeScreen();

            userWelcomeScreen.ConnectToServer = new Action<string, int>((serverIP, serverPort) => {
                if (serverIP.Equals(GetPublicIP())) {
                    client.Connect("127.0.0.1", serverPort);
                } else {
                    client.Connect(serverIP, serverPort);
                }
            });

            userWelcomeScreen.LoginAction = new Action<string, string>((user, pass) => {
                CommandBuilder.Login(user, pass).Send(client);
                this.Username = user;
                string LoginLog = SuccesfullLogin();
                if (!LoginLog.Equals("succesfull")) {
                    if (LoginLog.Equals("error:0")) {
                        MessageBox.Show("Invalid login information !");
                    } else if (LoginLog.Equals("error:1")) {
                        MessageBox.Show("Someone already login with your username and password !");
                    }
                } else {
                    CommandBuilder.LoginSuccesfull(client).Send(client);
                    userWelcomeScreen.Dispose();
                }
            });

            userWelcomeScreen.RegisterAction = new Action<string, string>((user, pass) => {
                CommandBuilder.Register(user, pass).Send(client);
                this.Username = user;
                string RegistrationLog = SuccesfullRegistred();
                if (!RegistrationLog.Equals("succesfull")) {
                    if (RegistrationLog.Equals("error:0")) {
                        MessageBox.Show("Username already exist !");
                    }
                }
            });

            userWelcomeScreen.ShowDialog();

            this.Text += " - Welcome " + Username;
            lblPlayer1Name.Text = Username;

            Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
            clientThread.Start(client);
        }

        public string SuccesfullLogin() {
            NetworkStream s = client.GetStream();
            string readData = ReadData(s);
            GameResponseParser parser = new GameResponseParser();
            Response response = parser.Parse(readData);
            if (response.Code == 0 && response.CommandName == "login") {
                if (response.Payload.Trim().Equals("failed:0")) {
                    return "error:0";
                } else if (response.Payload.Trim().Equals("failed:1")) {
                    return "error:1";
                }
            }
            return "succesfull";
        }

        public string SuccesfullRegistred() {
            NetworkStream s = client.GetStream();
            string readData = ReadData(s);
            GameResponseParser parser = new GameResponseParser();
            Response response = parser.Parse(readData);
            if (response.Code == 0 && response.CommandName == "register") {
                if (response.Payload.Trim().Equals("failed:0")) {
                    return "error:0";
                }
            }
            return "succesfull";
        }

        private void HandleClientComm(object client) {
            string responseData;

            while (true) {
                TcpClient tcpClient = (TcpClient)client;
                NetworkStream clientStream = tcpClient.GetStream();

                responseData = ReadData(clientStream);

                if (IsResponseDataComplete(responseData)) {
                    responseData = responseData.TrimEnd(';');
                    GameResponseParser parser = new GameResponseParser();

                    Response response = parser.Parse(responseData);

                    IResponseRunner runner = ResponseRunnerRegistry.GetReponseRunner(response.CommandName);

                    runner.RunResponse(this, response);
                    //&#59;
                    responseData = string.Empty;
                }
            }
        }

        private bool IsResponseDataComplete(string responseData) {
            return responseData.EndsWith(";");
        }

        static string ReadData(NetworkStream clientStream) {
            byte[] message = new byte[4096];
            int bytesRead;

            try {
                //blocks until a client sends a message
                bytesRead = clientStream.Read(message, 0, 4096);
            } catch {
                //a socket error has occured
                return null;
            }

            if (bytesRead == 0) {
                //the client has disconnected from the server
                return null;
            }

            //message has successfully been received
            ASCIIEncoding encoder = new ASCIIEncoding();
            return encoder.GetString(message, 0, bytesRead);
        }
        public static byte[] StrToByteArray(string str) {
            System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
            return encoding.GetBytes(str);
        }

        public void AppendText(string text) {
            if (string.IsNullOrEmpty(text))
                return;
            if (txtOutputText.InvokeRequired) {
                txtOutputText.Invoke(new Action<string>(AppendText), text);
            } else {
                txtOutputText.Text = text;
            }
        }

        public void ShowMessage(string text) {
            MessageBox.Show(text, "Info");
        }

        private void createAGameToolStripMenuItem_Click(object sender, EventArgs e) {
            InputBox getInfo = new InputBox();
            getInfo.lblInputDescription.Text = "Room name";
            getInfo.Text = "Enter a room name";
            if (getInfo.ShowDialog(this) == DialogResult.OK) {
                RoomName = getInfo.txtInputBox.Text;
                CommandBuilder.CreateRoom(RoomName).Send(client);
                pnlwaitingForOpponent.Visible = true;
                MCreateAGame.Enabled = false;
                MSearchForAGame.Enabled = false;
            }
        }

        private void searchForAGameToolStripMenuItem_Click(object sender, EventArgs e) {
            CommandBuilder.SearchForARoom().Send(client);
            MSearchForAGame.Enabled = false;
            MCreateAGame.Enabled = false;
        }

        public void NoRoomsFound() {
            if (this.InvokeRequired) {
                this.Invoke(new Action(NoRoomsFound));
                return;
            }
            MSearchForAGame.Enabled = true;
            MCreateAGame.Enabled = true;
        }

        private void waitingForOpponent_Tick(object sender, EventArgs e) {
            lblAnimatedWaiting.Text += ".";
            if (lblAnimatedWaiting.Text.Length > 3) {
                lblAnimatedWaiting.Text = ".";
            }
        }

        public void OponnentConnected(string OponnentUsername) {
            if (this.InvokeRequired) {
                this.Invoke(new Action<string>(OponnentConnected), OponnentUsername);
                return;
            }

            lblPlayer2Name.Text = OponnentUsername;
            pnlwaitingForOpponent.Visible = false;
            pnlOpponent.Visible = true;
            pnlOpponent.Enabled = false;
            pnlInfo.Visible = true;
            pnlSelectedFields.Visible = true;
            pnlPlayer1Map.Enabled = true;
            pnlPlayer2Map.Visible = true;
            TSMSurrender.Enabled = true;
        }

        public void SetTurn() {
            if (pnlOpponent.InvokeRequired) {
                pnlOpponent.Invoke(new MethodInvoker(delegate { pnlOpponent.Enabled = true; }));
            } else {
                pnlOpponent.Enabled = true;
            }
        }

        public void WaitTurn() {
            if (pnlOpponent.InvokeRequired) {
                pnlOpponent.Invoke(new MethodInvoker(delegate { pnlOpponent.Enabled = false; }));
            } else {
                pnlOpponent.Enabled = false;
            }
        }

        private void SelectField(object sender, EventArgs e) {
            Button btn = (Button)sender;
            if (btn.BackColor != Color.Green) {
                nrOfSelectedFields++;
                do {
                    btn.BackColor = Color.Green;
                    lblFieldSelection.Text += btn.Text + " ";
                    SelectedFields.Add(btn.Text);
                } while (nrOfSelectedFields == 4);
            }
            if (SelectedFields.Count == 3) {
                btnStartGame.Enabled = true;
                pnlPlayer1Map.Enabled = false;
            } else {
                btnStartGame.Enabled = false;
                pnlPlayer1Map.Enabled = true;
            }
        }

        private void btnStartGame_Click(object sender, EventArgs e) {
            btnStartGame.Enabled = false;
            string fieldSelected = string.Empty;
            foreach (var item in SelectedFields) {
                fieldSelected += item + ":";
            }
            Command rdy = new Command();
            NetworkStream s = client.GetStream();
            rdy.CommandName = "selectfield";
            rdy.Argument = Username;
            rdy.Message = fieldSelected;
            string rdyComm = rdy.ToString();
            s.Write(StrToByteArray(rdyComm), 0, rdyComm.Length);
            pnlInfo.Visible = false;
        }

        public void GameHasStart() {
            if (this.InvokeRequired) {
                this.Invoke(new Action(GameHasStart));
                return;
            }
            btnStartGame.Visible = false;
            pnlPlayer1Map.Enabled = false;
            pnlPlayer2Map.Enabled = true;
            pnlOpponent.Visible = true;

        }

        private void hitOpponentField(object sender, EventArgs e) {
            NetworkStream s = client.GetStream();
            Button btn = (Button)sender;
            Command cmd = new Command();
            cmd.CommandName = "hitfield";
            cmd.Argument = btn.Text;
            cmd.Message = Username;
            string hitComm = cmd.ToString();
            s.Write(StrToByteArray(hitComm), 0, hitComm.Length);

        }
        public void HitChangePanel1(string field) {
            foreach (Control control in pnlPlayer1Map.Controls) {
                if (control is Button) {
                    if (control.Text.Equals(field.Trim())) {
                        control.BackColor = Color.Red;
                    }
                }
            }
        }
        public void HitChangePanel2(string field) {
            foreach (Control control in pnlPlayer2Map.Controls) {
                if (control is Button) {
                    if (control.Text.Equals(field.Trim())) {
                        control.BackColor = Color.Red;
                    }
                }
            }
        }
        public void MissChangePanel1(string field) {
            foreach (Control control in pnlPlayer1Map.Controls) {
                if (control is Button) {
                    if (control.Text.Equals(field.Trim())) {
                        control.BackColor = Color.Yellow;
                    }
                }
            }
        }
        public void MissChangePanel2(string field) {
            foreach (Control control in pnlPlayer2Map.Controls) {
                if (control is Button) {
                    if (control.Text.Equals(field.Trim())) {
                        control.BackColor = Color.Yellow;
                    }
                }
            }
        }
        public void EndGame() {
            if (this.InvokeRequired) {
                this.Invoke(new Action(EndGame));
                return;
            }

            txtOutputText.Text = String.Empty;
            btnStartGame.Visible = true;
            btnStartGame.Enabled = false;
            pnlPlayer2Map.Visible = false;
            pnlPlayer2Map.Enabled = false;
            pnlPlayer1Map.Enabled = false;
            pnlOpponent.Visible = false;
            MCreateAGame.Enabled = true;
            MSearchForAGame.Enabled = true;
            lblFieldSelection.Visible = false;
            pnlwaitingForOpponent.Visible = false;
            lblAnimatedWaiting.Visible = false;
            pnlInfo.Visible = false;
            TSMSurrender.Enabled = false;

            nrOfSelectedFields = 0;
            SelectedFields = new List<string>();

            foreach (Control control in pnlPlayer1Map.Controls) {
                if (control is Button) {
                    control.BackColor = SystemColors.Highlight;
                }
            }
            foreach (Control control in pnlPlayer2Map.Controls) {
                if (control is Button) {
                    control.BackColor = SystemColors.Highlight;
                }
            }
        }

        public void ExitGame() {
            Environment.Exit(0);
        }
        private void BattleShips_FormClosed(object sender, FormClosedEventArgs e) {
            CommandBuilder.ExitGame().Send(client);
        }
        private void BattleShips_FormClosing(object sender, FormClosingEventArgs e) {
            CommandBuilder.Surrender().Send(client);
        }
        private void aboutToolStripMenuItem1_Click(object sender, EventArgs e) {
            AboutBox aboutBox = new AboutBox();
            if (aboutBox.ShowDialog() == DialogResult.OK) {
                aboutBox.Dispose();
            }
        }

        private void howToToolStripMenuItem_Click(object sender, EventArgs e) {
            HowToPlay howToPlay = new HowToPlay();
            if (howToPlay.ShowDialog() == DialogResult.OK) {
                howToPlay.Dispose();
            }
        }

        private void TSMExit_Click(object sender, EventArgs e) {
            CommandBuilder.Surrender().Send(client);
            CommandBuilder.ExitGame().Send(client);
        }

        private void TSMSurrender_Click(object sender, EventArgs e) {
            CommandBuilder.Surrender().Send(client);
        }

        public string GetPublicIP() {
            string direction = "";
            WebRequest request = WebRequest.Create("http://checkip.dyndns.org/");
            using (WebResponse response = request.GetResponse())
            using (StreamReader stream = new StreamReader(response.GetResponseStream())) {
                direction = stream.ReadToEnd();
            }

            //Search for the ip in the html
            int first = direction.IndexOf("Address: ") + 9;
            int last = direction.LastIndexOf("</body>");
            direction = direction.Substring(first, last - first);

            return direction;
        }
    }
}
